﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.IO;
using System.Threading;
using System.Xml;
using System.Reflection;

using LongNet.Util;
using LongNet.Web;
namespace LongNet.Config
{
    /// <summary>
    /// Handles the access to certain configuration sections. 
    /// It is for configuring LongNet from configuration file and getting the current LongNet configuration.
    /// </summary>
    public class ConfigSectionHandler : IConfigurationSectionHandler
    {
        #region Private Fields

        private static LongNet.Util.LogManager.Logger logger = LongNet.Util.LogManager.GetCurrentClassLogger();

        private static bool configLoaded = false;
        private static FileWatcher configWatcher = null;
        private static LongNetConfigSection config = null;

        private static object syncRoot = new object();

        #endregion

        #region Private Static Methods

        private static void ConfigFileChanged(object sender, FileSystemEventArgs args)
        {
            lock (syncRoot)
            {
                configWatcher.StopWatching();
                try
                {
                    Thread.Sleep(10);
                    LongNetConfigSection newConfig = config.Reload();

                    if (newConfig != null)
                    {
                        if (logger.IsDebugEnabled)
                        {
                            logger.Debug("Configuration reloaded successfully.");
                        }

                        config = newConfig;

                        // reset other config
                        //MemcachedClient.InitConfig(config.CachingConfig.Memcached);
                        //SqlServerHelper.InitConfig(config.DataConfig.SqlServerHelper);
                        //MtvFramework.InitConfig(config.WebConfig.Mtv);
                        VerifyImage.InitConfig(config.WebConfig.VerifyImage);
                        //Rewriter.InitConfig(config.WebConfig.UrlRewriting);
                    }
                    else
                    {
                        throw new Exception("Configuration.Reload() returned null. Not reloading.");
                    }
                    configWatcher.StartWatching();
                }
                catch (Exception ex)
                {
                    configWatcher.StartWatching();

                    if (logger.IsDebugEnabled)
                    {
                        logger.Error(ex);
                    }
                    throw ex;
                }
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the current LongNet configuration.
        /// </summary>
        public static LongNetConfigSection Configuration
        {
            get
            {
                lock (syncRoot)
                {
                    if (configLoaded)
                        return config;

                    #region load config

                    configLoaded = true;
                    if (config == null)
                    {
                        // gets the default configuration by parsing 
                        // the application configuration file (web.config or app.exe.config)
                        config = ConfigurationManager.GetSection(LongNetConfigSection.ConfigSectionName) as LongNetConfigSection;
                    }
                    if (config == null)
                    {
                        // gets the configuration from app-root-dir/LongNet.config
                        string configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LongNet.config");
                        if (File.Exists(configFile))
                        {
                            if (logger.IsDebugEnabled)
                                logger.Debug("Attempting to load config from {0}", configFile);

                            config = new LongNetConfigSectionXml(configFile);
                        }
                    }
                    if (config == null)
                    {
                        // gets the configuration from app-root-dir/(web.LongNet.config or app.LongNet.config)
                        string configFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
                        if (configFile != null)
                        {
                            configFile = Path.ChangeExtension(configFile, ".LongNet.config");
                            if (File.Exists(configFile))
                            {
                                if (logger.IsDebugEnabled)
                                    logger.Debug("Attempting to load config from {0}", configFile);

                                config = new LongNetConfigSectionXml(configFile);
                            }
                        }
                    }
                    if (config == null)
                    {
                        // gets the configuration from (dir of LongNet.dll)/LongNet.dll.config
                        Assembly assembly = typeof(LongNetConfigSection).Assembly;
                        if (!assembly.GlobalAssemblyCache)
                        {
                            string configFile = assembly.Location + ".config";
                            if (File.Exists(configFile))
                            {
                                if (logger.IsDebugEnabled)
                                    logger.Debug("Attempting to load config from {0}", configFile);

                                config = new LongNetConfigSectionXml(configFile);
                            }
                        }
                    }

                    if (config == null)
                    {
                        // gets the configuration from environment variable : LongNet_GLOBAL_CONFIG_FILE
                        string configFile = Utils.GetEnvironmentVariable("LONGNET_GLOBAL_CONFIG_FILE");

                        if (!string.IsNullOrEmpty(configFile))
                        {
                            if (File.Exists(configFile))
                            {
                                if (logger.IsDebugEnabled)
                                    logger.Debug("Attempting to load config from {0}", configFile);

                                config = new LongNetConfigSectionXml(configFile);
                            }
                            else
                            {
                                if (logger.IsWarnEnabled)
                                    logger.Warn("LongNet global config file pointed by LONGNET_GLOBAL_CONFIG_FILE '{0}' doesn't exist.", configFile);
                            }
                        }
                    }

                    #endregion

                    // failed to load config
                    if (config == null)
                        throw new LongNetConfigException("Failed to load LongNet configuration.");

                    // watch config file
                    if (configWatcher == null && !string.IsNullOrEmpty(config.ConfigPath))
                    {
                        configWatcher = new FileWatcher(config.ConfigPath, ConfigFileChanged);
                        configWatcher.StartWatching();
                    }

                    return config;
                }
            }
        }

        #endregion

        #region IConfigurationSectionHandler

        /// <summary>
        /// Creates a configuration section handler.
        /// </summary>
        /// <param name="parent">Parent object.</param>
        /// <param name="configContext">Configuration context object.</param>
        /// <param name="section">Section XML node.</param>
        /// <returns>The created section handler object.</returns>
        object IConfigurationSectionHandler.Create(object parent, object configContext, XmlNode section)
        {
            try
            {
                string configPath = Utils.AppConfigurationFile;

                return new LongNetConfigSectionXml((XmlElement)section, configPath);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion
    }
}
